package engine

import (
	"learn/crawler/fetcher"
	"log"
)

type Engine struct {
	Scheduler   Scheduler
	WorkerCount int
	ItemChan    chan Item
}

type Scheduler interface {
	ReadyNotifier
	Submit(Request)
	WorkerChan() chan Request
	Run()
}

type ReadyNotifier interface {
	WorkerReady(chan Request)
}

func (e *Engine) Run(seeds ...Request) {
	out := make(chan ParseResult)
	e.Scheduler.Run()

	// 创建Workers到worker queue中
	for i := 0; i < e.WorkerCount; i++ {
		createWorker(e.Scheduler.WorkerChan(), out, e.Scheduler)
	}

	// 增加需要请求的Requests
	for _, r := range seeds {
		e.Scheduler.Submit(r)
	}

	for {
		result := <-out
		// 将获取的items发送到ItemChan中
		for _, item := range result.Item {
			go func(item Item) { e.ItemChan <- item }(item)
		}

		// 将获取结果中新的Requests添加到scheduler中继续新的请求
		for _, r := range result.Requests {
			e.Scheduler.Submit(r)
		}
	}
}

func createWorker(in chan Request, out chan ParseResult, ready ReadyNotifier) {
	go func() {
		for {
			ready.WorkerReady(in)
			request := <-in
			result, err := Worker(request)
			if err != nil {
				continue
			}

			out <- result
		}
	}()
}

// 根据Request去获取数据并解析成ParseResult
func Worker(r Request) (ParseResult, error) {
	log.Printf("fetching %s", r.Url)
	body, err := fetcher.Fetch(r.Url)
	if err != nil {
		log.Printf("fetcher error fetching url "+
			"%s: %v", r.Url, err)
		return ParseResult{}, err
	}

	return r.ParserFunc(body), nil
}
